import { Graph, Cell, CellView, Node, Shape } from '@antv/x6';
import { message } from 'antd';
import { NodeHanlder, Point, Register } from '../../types';
import {
  autoSaveJson,
  defaultEdgeShapeName,
  defaultModuleNodeName,
} from '../../tools';
const minimumLaneHeight = 50;

const nodeHanlderMap: Map<string, NodeHanlder> = new Map();

// 获取节点样式
export function getNodeAttr(shape: string | undefined, data: any, assets: any) {
  if (!shape) return {};
  const handler = nodeHanlderMap.get(shape);
  if (handler) {
    return handler(data, assets);
  }
  return {};
}
// 注册节点和线条的样式
export function registerAll(list?: Register[]) {
  if (!Array.isArray(list) || list.length === 0) return;
  list.forEach(({ name, options, type, handler }) => {
    if (type === 'NODE' && handler) {
      nodeHanlderMap.set(name, handler);
      Graph.registerNode(name, options, true);
    } else if (type === 'EDGE') {
      Graph.registerEdge(name, options, true);
    }
  });
}
// 默认模型组件样式
function getDefaultModuleNodeAttr(data: any, assets: any) {
  const { icon } = data;
  const image = assets[icon] || icon;
  return {
    icon: { xlinkHref: image },
  };
}

// 注册一个默认的样板组件
export function registerModuleNode() {
  nodeHanlderMap.set(defaultModuleNodeName, getDefaultModuleNodeAttr);
  Graph.registerNode(
    defaultModuleNodeName,
    {
      inherit: 'rect',
      width: 190,
      height: 32,
      markup: [
        {
          tagName: 'rect',
          selector: 'body',
        },
        {
          tagName: 'text',
          selector: 'text',
        },
        {
          tagName: 'image',
          selector: 'icon',
        },
      ],
      attrs: {
        body: {
          strokeWidth: 0,
          stroke: '#5F95FF',
          fill: '#fff',
        },
        icon: {
          ref: 'body',
          refX: 10,
          refY: 8,
          width: 16,
          height: 16,
        },
        text: {
          refX: 36,
          refY: 17,
          fontSize: 12,
          fill: '#333',
          textAnchor: 'start',
        },
      },
    },
    true,
  );
}
// 泳道样式
export function registerLane(config: { width?: number; height?: number } = {}) {
  const { width = 500, height = 200 } = config;
  Graph.registerNode(
    'lane',
    {
      width,
      height,
      inherit: 'rect',
      markup: [
        {
          tagName: 'rect',
          selector: 'body',
          className: 'laneBody',
        },
        {
          tagName: 'rect',
          selector: 'nameRect',
          className: 'laneRect',
        },
        {
          tagName: 'text',
          selector: 'nameText',
          className: 'laneName',
        },
        {
          tagName: 'rect',
          selector: 'bottomBorder',
          className: 'resizeBorder',
        },
      ],
      attrs: {
        body: {
          fill: 'transparent',
          stroke: '#f00',
          strokeWidth: 0,
          cursor: 'default',
        },
        nameRect: {
          width: 30,
          refHeight: '100%',
          fill: '#EAFEEC',
          stroke: '#fff',
          strokeWidth: 0,
          x: 0,
          cursor: 'default',
        },
        nameText: {
          ref: 'nameRect',
          textAnchor: 'middle',
          fontWeight: 'bold',
          fill: '#6A8E67',
          fontSize: 12,
          cursor: 'default',
        },
        bottomBorder: {
          refWidth: '100%',
          height: 2,
          stroke: '#9BD787',
          refY: '100%',
          refX: 0,
          y: -3,
          cursor: 'row-resize',
        },
      },
    },
    true,
  );
}
// 默认线条
export function registerEdge() {
  Graph.registerEdge(
    defaultEdgeShapeName,
    {
      inherit: 'edge',
      attrs: {
        line: {
          stroke: '#A2B1C3',
          strokeWidth: 2,
        },
      },
      label: {
        attrs: {
          label: {
            fill: '#A2B1C3',
            fontSize: 12,
          },
        },
      },
    },
    true,
  );
}

function scroll(startPoint, currentPoint, xAxisRef, yAxisRef) {
  const { x: sx, y: sy } = startPoint || { x: 0, y: 0 };
  const { x: cx, y: cy } = currentPoint;
  const [ox, oy] = [sx - cx, sy - cy];
  xAxisRef.current.scrollLeft = xAxisRef.current.scrollLeft + ox;
  yAxisRef.current.scrollTop = yAxisRef.current.scrollTop + oy;
  startPoint.x = cx;
  startPoint.y = cy;
}

// 拖拽移动画面
export function registerDragGraph(graph: Graph, xAxisRef, yAxisRef) {
  let startPoint: Point | undefined;
  graph.on('node:mousedown', ({ e, cell }) => {
    const { target, clientX: x, offsetY: y } = e;
    const {
      data: { type },
    } = cell;
    if (target && type === 'lane') {
      startPoint = { x, y };
    }
  });
  graph.on('node:mousemove', ({ e, cell }) => {
    const { target, clientX: x, clientY: y } = e;
    const {
      data: { type },
    } = cell;
    if (target && type === 'lane') {
      scroll(startPoint, { x, y }, xAxisRef, yAxisRef);
    }
  });
  graph.on('node:mouseup', () => {
    startPoint = undefined;
  });
  graph.on('blank:mousedown', ({ e }) => {
    const { clientX: x, clientY: y } = e;
    startPoint = { x, y };
  });
  graph.on('blank:mousemove', ({ e }) => {
    const { clientX: x, clientY: y } = e;
    scroll(startPoint, { x, y }, xAxisRef, yAxisRef);
  });
  graph.on('blank:mouseup', () => {
    startPoint = undefined;
  });
  yAxisRef.current.addEventListener('mousewheel', mouseScroll);
  // 鼠标滚轮左右滚动
  function mouseScroll(e) {
    const { wheelDeltaX: x, wheelDeltaY: y } = e;
    scroll({ x: 0, y: 0 }, { x, y }, xAxisRef, yAxisRef);
  }
  return function () {
    if (yAxisRef.current) {
      yAxisRef.current.removeEventListener('mousewheel', mouseScroll);
    }
  };
}
// 控制连接桩显示/隐藏
const showPorts = (ports: NodeListOf<SVGElement>, show: boolean) => {
  if (!ports) return;
  for (let i = 0, len = ports.length; i < len; i = i + 1) {
    ports[i].style.visibility = show ? 'visible' : 'hidden';
  }
};
// 注册泳道的拖拽改变尺寸
export function registerResizeLane(
  graph: Graph,
  onLaneResize: (
    dragNode: Node,
    width: number,
    height: number,
    minHeight: number,
  ) => void,
) {
  let dragNode: Node | undefined = undefined;
  let offsetY: number | undefined = undefined;
  let oHeight: number | undefined = undefined;
  let minHeight: number = minimumLaneHeight;
  graph.on('node:mousedown', ({ e, x, y, cell, node }) => {
    const { target } = e;
    if (target && target.className.baseVal === 'resizeBorder') {
      dragNode = cell;
      offsetY = y;
      oHeight = node.size().height;
      const children = node.getChildren() as Node[];
      if (Array.isArray(children) && children.length) {
        minHeight = Math.max(...children.map((child) => child.size().height));
      }
    }
  });
  graph.on('node:mousemove', ({ y, node }) => {
    if (!dragNode) return;
    const { width } = node.size();
    const height = (oHeight || 0) + y - (offsetY || 0);
    onLaneResize && onLaneResize(dragNode, width, height, minHeight);
  });
  graph.on('node:mouseup', () => {
    dragNode = undefined;
    offsetY = undefined;
    oHeight = undefined;
    minHeight = minimumLaneHeight;
  });
}
// 节点双击事件
export function registerDbClickNode(
  graph: Graph,
  setCurrentCell: (node: Cell) => void,
  handlerActions?: (key: string) => void,
) {
  graph.on('node:dblclick', ({ node }) => {
    if (node.data.type !== 'node') return;
    setCurrentCell(node);
    handlerActions && handlerActions('edite');
  });
}
// 节点连线点显示的事件
export function registerResizeNode(graph: Graph) {
  graph.on('node:mouseenter', ({ node, view }) => {
    if (node.data.type !== 'node') return;
    const container = view.container;
    const ports = container.querySelectorAll(
      '.x6-port-body',
    ) as NodeListOf<SVGElement>;
    showPorts(ports, true);
  });
  graph.on('node:mouseleave', ({ node, view }) => {
    if (node.data.type !== 'node') return;
    const container = view.container;
    const ports = container.querySelectorAll(
      '.x6-port-body',
    ) as NodeListOf<SVGElement>;
    showPorts(ports, false);
  });
}
// 右键显示按钮
export function registerNodeMenuContext(
  graph: Graph,
  menuRef: any,
  callback?: (node: any) => void,
) {
  graph.on('node:contextmenu', ({ e, x, y, cell }) => {
    if (cell.data.type === 'lane') return;
    const menuContainer = menuRef.current;
    const { clientWidth, clientHeight } = menuContainer;
    menuContainer.style.left = `${x - clientWidth / 2}px`;
    menuContainer.style.top = `${y - clientHeight / 2}px`;
    callback && callback(cell);
  });
}
// 监听元素变化（节点和线）
export function registerAutoSave(graph: Graph, callback?: (data: any) => void) {
  graph.on('cell:added', async () => {
    await autoSaveJson(null, graph, callback);
  });
  graph.on('cell:removed', async () => {
    await autoSaveJson(null, graph, callback);
  });
  graph.on('cell:changed', async () => {
    await autoSaveJson(null, graph, callback);
  });
}
// 删除连线
export function registerEdgeRemove(
  graph: Graph,
  callback?: (edge: any) => void,
) {
  graph.on('edge:mouseenter', ({ edge }) => {
    edge.addTools([
      {
        name: 'button-remove',
        args: {
          distance: -40,
          onClick({ view }: any) {
            const edge = view.cell;
            callback && callback(edge);
          },
        },
      },
    ]);
  });

  graph.on('edge:mouseleave', ({ edge }) => {
    edge.removeTools();
  });
}
// 节点数量发生变化时
export function registerNodeNumChange(graph: Graph) {
  graph.on('node:added', ({ node }) => {
    if (!node.parent) return;
    const { width: nw } = node.size();
    const { clientWidth: gw } = graph.container;
    const lane = graph.getCellById(node.parent.id);
    const nodes = lane.getChildren() as Node[];
    const maxWidth = Math.max(
      gw,
      ...(nodes || []).map((n) => n.position().x + n.size().width + nw + 100),
    );
    if (maxWidth !== gw) {
      graph
        .getNodes()
        .filter((n) => n.shape === 'lane')
        .forEach((laneNode) => {
          const { height: lh } = laneNode.size();
          laneNode.size(maxWidth, lh);
        });
      graph.container.style.width = maxWidth + 'px';
    }
  });
}
// 监听history变化
export function registerHistory(
  graph: Graph,
  configs: {
    setCanRedo: (canRedo: boolean) => void;
    setCanUndo: (canUndo: boolean) => void;
    onUndoOrRedo?: (
      type: 'undo' | 'redo',
      graph: Graph,
      args: { cmds: any[]; options: any },
    ) => void;
  },
) {
  const { setCanRedo, setCanUndo, onUndoOrRedo } = configs;
  // 节点和边的添加和移除可能涉及到后端交互，因此这里交给开发者处理，组件不做处理
  graph.history.on('undo', (args: { cmds: any[]; options: any }) => {
    onUndoOrRedo && onUndoOrRedo('undo', graph, args);
  });
  graph.history.on('redo', (args: { cmds: any[]; options: any }) => {
    onUndoOrRedo && onUndoOrRedo('redo', graph, args);
  });
  graph.history.on('change', () => {
    setCanRedo(graph.history.canRedo());
    setCanUndo(graph.history.canUndo());
  });
}
// 泳道高度调整
export function registerLaneCellChange(
  cell: Cell,
  onLaneSizeChanged: () => void,
) {
  cell.on('change:size', () => onLaneSizeChanged());
}
// 注册快捷键
export function registerKeyboard(
  graph: Graph,
  onActions?: (action: string) => void,
) {
  // #region 快捷键与事件
  // copy
  graph.bindKey(['meta+c', 'ctrl+c'], () => {
    onActions && onActions('copy');
    return false;
  });
  // paste
  graph.bindKey(['meta+v', 'ctrl+v'], () => {
    onActions && onActions('paste');
    return false;
  });

  //undo
  graph.bindKey(['meta+z', 'ctrl+z'], () => {
    onActions && onActions('undo');
    return false;
  });
  // redo
  graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
    onActions && onActions('redo');
    return false;
  });

  //delete
  graph.bindKey('backspace', () => {
    onActions && onActions('delete');
  });

  // zoom bigger
  graph.bindKey(['ctrl+1', 'meta+1'], () => {
    onActions && onActions('bigger');
  });
  // zoom smaller
  graph.bindKey(['ctrl+2', 'meta+2'], () => {
    onActions && onActions('smaller');
  });
  // clearStorage
  graph.bindKey(['ctrl+e', 'meta+e'], () => {
    onActions && onActions('clearStorage');
  });
}
// 初始化画布
export function getGraph(
  container: HTMLDivElement,
  minimapContainer: HTMLDivElement | null | undefined,
  config?: any,
) {
  const { onAddLink, defaultEdgeShape } = config || {};
  const graph = new Graph({
    panning: false,
    keyboard: true,
    history: {
      enabled: true,
      ignoreAdd: false,
      ignoreRemove: false,
      ignoreChange: false,
      beforeAddCommand(event: string, args: any) {
        const { key, cell } = args;
        if (args.key === 'tools') return false;
        // 数据如果影响到节点样式会被记录到撤销队列，但是撤销的时候只能恢复样式，无法恢复数据
        // 因此禁止节点数据更新被记录到队列中
        if (event === 'cell:change:*' && cell.isNode() && key === 'attrs') {
          return false;
        }
        if (cell.isEdge()) {
          if (!cell.data) return false;
          const { source, target } = cell.data;
          if (!source || !target) return false;
        }
        return true;
      },
    },
    grid: {
      size: 10,
      visible: true,
      type: 'mesh',
    },
    container: container,
    selecting: {
      enabled: true,
      filter(node) {
        return node.shape !== 'lane';
      },
    },
    clipboard: true,
    resizing: {
      enabled: (cell: any) => {
        return cell.type === 'node';
      },
    },
    connecting: {
      allowNode: (options: any) => {
        const { targetCell } = options;
        return targetCell.type === 'node';
      },
      router: 'manhattan',
      connector: {
        name: 'rounded',
        args: {
          radius: 8,
        },
      },
      anchor: 'center',
      allowBlank: false,
      allowEdge: false,
      allowLoop: false,
      allowMulti: false,
      snap: {
        radius: 20,
      },
      createEdge() {
        return new Shape.Edge({
          shape: defaultEdgeShape,
          attrs: {
            line: {
              stroke: '#A2B1C3',
              strokeWidth: 2,
            },
          },
        });
      },
      validateEdge({ edge }) {
        const sourceCell = edge.getSourceCell();
        const targetCell = edge.getTargetCell();
        if (
          !sourceCell ||
          !targetCell ||
          !sourceCell.parent ||
          !targetCell.parent
        ) {
          return false;
        }
        const sourceParentSort = sourceCell.parent.data.sort;
        const targetParentSort = targetCell.parent.data.sort;
        // 只能相邻层级节点进行连线
        if (Math.abs(sourceParentSort - targetParentSort) !== 1) {
          message.info('只能对相邻的层级进行连线');
          return false;
        }
        edge.data = {
          id: `${sourceCell.id}_${targetCell.id}`,
          type: 'link',
        };
        onAddLink && onAddLink(edge);
        return false;
      },
    },
    translating: {
      restrict(cellView: CellView) {
        const cell = cellView.cell as Node;
        const parentId = cell.prop('parent');
        if (parentId) {
          const parentNode = graph.getCellById(parentId) as Node;
          if (parentNode) {
            return parentNode.getBBox().moveAndExpand({
              x: 32,
              y: 0,
              width: -32,
              height: -4,
            });
          }
        }
        return cell.getBBox();
      },
    },
  });

  return graph;
}
